package com.xsjiong.hoofen.entities;

import android.util.SparseArray;
import com.xsjiong.hoofen.IO;
import com.xsjiong.hoofen.Logs;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.*;

public class Exam implements DataEntity {
	private School school;
	private int yearIn;
	private long examId;
	private Date examDate;
	// TODO 这里原本有一个我推断这里的classID是和gradeID有联系的，但不排除有特例，望修复
	private int[] clsIds;
	// 这里原本有一个examSort的变量，不明用途
	private String examName;
	private int xkId;
	private int examType; //  模考1：单元测试  2：月考  3：模拟考试 4：期中考试  5：期末考试

	void setSchool(School s) {
		this.school = s;
	}

	private Exam() {
	}

	public Exam(School school, long examId, Date examDate, long[] clsIds, String examName, int xkId, int examType) {
		this.school = school;
		this.yearIn = (int) ((clsIds[0] / 1000) % 100);
		this.examId = examId;
		this.examDate = examDate;
		this.clsIds = new int[clsIds.length];
		for (int i = 0; i < this.clsIds.length; i++) this.clsIds[i] = (int) (clsIds[i] % 1000);
		Arrays.sort(clsIds);
		this.examName = examName;
		this.xkId = xkId;
		this.examType = examType;
	}

	public Exam(School school, long examId, long examDate, long[] clsIds, String examName, int xkId, int examType) {
		this(school, examId, new Date(examDate), clsIds, examName, xkId, examType);
	}

	@Override
	public void writeTo(DataOutputStream out) throws IOException {
		out.writeInt(school.getID());
		out.writeInt(yearIn);
		out.writeLong(examId);
		out.writeLong(examDate.getTime());
		out.writeInt(clsIds.length);
		for (int i = 0; i < clsIds.length; i++) out.writeInt(clsIds[i]);
		out.writeBytes(examName);
		out.writeInt(xkId);
		out.writeInt(examType);
	}

	@Override
	public void readFrom(DataInputStream in) throws IOException {
		school = School.getInstance(in.readInt());
		yearIn = in.readInt();
		examId = in.readLong();
		examDate = new Date(in.readLong());
		clsIds = new int[in.readInt()];
		for (int i = 0; i < clsIds.length; i++) clsIds[i] = in.readInt();
		examName = in.readUTF();
		xkId = in.readInt();
		examType = in.readInt();
	}

	@Override
	public void readFrom(JSONObject obj) {
		yearIn = obj.optInt("yearIn");
		examId = obj.optLong("examId");
		examDate = new Date(obj.optLong("examDate"));
		// MMP! 为什么TMD他用String存！！！
		String tmp = obj.optString("clsIds");
		String[] tmparr = tmp.substring(1, tmp.length() - 1).split(",");
		clsIds = new int[tmparr.length];
		for (int i = 0; i < tmparr.length; i++) clsIds[i] = (int) (Long.parseLong(tmparr[i]) % 1000);
		Arrays.sort(clsIds);
		examName = obj.optString("examName");
		xkId = obj.optInt("xkId", 0);
		examType = obj.optInt("examType");
	}

	@Override
	public JSONObject toJSON() throws JSONException {
		JSONObject ret = new JSONObject();
		ret.put("school", school.getID());
		ret.put("yearIn", yearIn);
		ret.put("examId", examId);
		ret.put("examDate", examDate.getTime());
		ret.put("clsIds", Arrays.toString(clsIds));
		ret.put("examName", examName);
		ret.put("xkId", xkId);
		ret.put("examType", examType);
		return ret;
	}

	public static Exam loadFrom(DataInputStream in) throws IOException {
		Exam ret = new Exam();
		ret.readFrom(in);
		return ret;
	}

	public static Exam loadFrom(JSONObject obj) {
		Exam ret = new Exam();
		ret.readFrom(obj);
		return ret;
	}

	/*@Override
	public String toString() {
		return examName + "[" + examId + "," + G.SUBJECT_NAMES[examType] + "," + Arrays.toString(clsIds) + "," + G.formatDate(examDate) + "]";
	}*/

	@Override
	public String toString() {
		return examName;
	}

	public long getClassId(int id) {
		return school.getID() * 100000 + yearIn * 1000 + id;
	}

	public boolean isGeneral() {
		return xkId == 0;
	}

	private String[] title;

	public String[] getTitle() {
		return title;
	}

	public boolean hasClass(int cls) {
		return containsValue(clsIds, cls);
	}

	public static boolean containsValue(int[] arr, int val) {
		int left = 0, right = arr.length - 1;
		int mid;
		while (left <= right) {
			mid = (left + right) >> 1;
			if (arr[mid] > val)
				right = mid - 1;
			else if (arr[mid] == val) return true;
			else left = mid + 1;
		}
		return false;
	}

	public int[] getClassIDs() {
		return clsIds;
	}

	public String getClassIDList() {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < clsIds.length; i++) {
			buffer.append(clsIds[i]);
			if (i != clsIds.length - 1) buffer.append(' ');
		}
		return buffer.toString();
	}

	public String getDescription(Student s) {
		StringBuffer buffer = new StringBuffer();
		buffer.append("姓名:").append(s.name).append('\n')
				.append("班级:").append(s.clsID).append("班\n\n");
		for (int i = 0; i < title.length; i++)
			buffer.append(title[i]).append("分数:").append(s.scores[i]).append('\n')
					.append(title[i]).append("班排:").append(s.clsRanks[i]).append('\n')
					.append(title[i]).append("总排:").append(s.totalRanks[i]).append("\n\n");
		if (title.length != 0) buffer.delete(buffer.length() - 2, buffer.length());
		return buffer.toString();
	}

	public Collection<Student> getScores(int[] clsIds) {
		ArrayList<Student> rret = new ArrayList<>();
		if (isGeneral()) {
			try {
				// TODO 原本参数里面还有一个gradeId
				JSONObject obj = new JSONArray(IO.URLString("http://www.doofen.com/doofen/" + school.getID() + "/rpt100/1001?examId=" + examId)).optJSONObject(0);
				JSONArray title = obj.optJSONArray("title");
				int xklen = title.length() + 1;
				this.title = new String[xklen];
				this.title[0] = "总分";
				for (int i = 0; i < title.length(); i++)
					this.title[i + 1] = title.optJSONObject(i).optString("xkName");
				JSONArray arr = obj.optJSONArray("stuScore");
				Student[] ret = new Student[arr.length()];
				float[] scores;
				int[] ranks;
				JSONArray rawScore;
				int clsID;
				SparseArray<ArrayList<Student>> stat = new SparseArray<>();
				ArrayList<Student> tmp;
				Arrays.sort(clsIds);
				int tot = -1;
				for (int i = 0, j; i < arr.length(); i++) {
					obj = arr.optJSONObject(i);
					clsID = obj.optInt("classCode");
					if (!containsValue(clsIds, clsID)) continue;
					tot++;
					ret[tot] = new Student(obj.optString("stuName"));
					scores = new float[xklen];
					ranks = new int[xklen];
					scores[0] = (float) obj.optDouble("stuMixScore");
					ranks[0] = obj.optInt("stuMixRank");
					rawScore = obj.optJSONArray("score");
					for (j = 1; j < xklen; j++) {
						scores[j] = (float) rawScore.optDouble((j - 1) << 1);
						ranks[j] = rawScore.optInt(((j - 1) << 1) + 1);
					}
					ret[tot].scores = scores;
					ret[tot].totalRanks = ranks;
					ret[tot].clsID = clsID;
					ret[tot].clsRanks = new int[xklen];
					tmp = stat.get(clsID);
					if (tmp == null) {
						tmp = new ArrayList<>();
						stat.put(clsID, tmp);
					}
					tmp.add(ret[tot]);
				}
				float lastScore;
				Student stu;
				int cur = 0;
				for (int i = 0, j, q; i < stat.size(); i++) {
					tmp = stat.valueAt(i);
					// 没有总分
					for (j = 0, lastScore = -1; j < xklen; j++) {
						Collections.sort(tmp, new XKComparator(j));
						for (q = 0; q < tmp.size(); q++) {
							stu = tmp.get(q);
							if (stu.scores[j] != lastScore) {
								lastScore = i;
								cur = q + 1;
							}
							stu.clsRanks[j] = cur;
						}
					}
				}
				for (int i = 0; i < tot; i++) rret.add(ret[i]);
				rret.add(ret[tot]);
				ret = null;
				stat = null;
				System.gc();
				return rret;
			} catch (Throwable t) {
				Logs.e("Doofen - getScores", t);
				return null;
			}
		} else {
			if (clsIds == null) clsIds = this.clsIds;
			StringBuffer clsList = new StringBuffer();
			long base = (long) school.getID() * 100000 + yearIn * 1000;
			for (int i = 0; i < clsIds.length; i++) {
				clsList.append(base + clsIds[i]);
				if (i != clsIds.length - 1) clsList.append(',');
			}
			try {
				JSONObject obj = new JSONObject(IO.URLString("http://www.doofen.com/doofen/" + school.getID() + "/rpt100/10110?examId=" + examId + "&xkId=" + xkId + "&classId=" + clsList.toString() + "&yearIn=" + yearIn));
				if (!obj.optBoolean("success", false)) return null;
				obj = obj.optJSONObject("data");
				JSONArray tmp = obj.optJSONArray("head");
				int len = tmp.length() - 5;
				this.title = new String[len];
				this.title[0] = "总分";
				for (int i = 1; i < len; i++)
					title[i] = tmp.optJSONObject(i + 5).optString("name");
				float[] scores;
				int[] totalRanks, clsRanks;
				JSONArray arr = obj.optJSONArray("data");
				Student[] ret = new Student[arr.length()];
				for (int i = 0, j; i < arr.length(); i++) {
					tmp = arr.optJSONArray(i);
					ret[i] = new Student(tmp.optString(1));
					ret[i].clsID = Integer.parseInt(tmp.optString(0));
					scores = new float[len];
					totalRanks = new int[len];
					clsRanks = new int[len];
					for (j = 0; j < len; j++) {
						scores[j] = Float.parseFloat(tmp.optString(j * 3 + 3));
						totalRanks[j] = tmp.optInt(j * 3 + 4);
						clsRanks[j] = tmp.optInt(j * 3 + 5);
					}
					ret[i].scores = scores;
					ret[i].totalRanks = totalRanks;
					ret[i].clsRanks = clsRanks;
				}
				for (int i = 0; i < ret.length; i++) rret.add(ret[i]);
				Collections.sort(rret, new XKComparator(0));
				return rret;
			} catch (Throwable t) {
				Logs.e("Doofen - getScores", t);
				return null;
			}
		}
	}

	private static class XKComparator implements Comparator<Student> {
		private int xk;

		XKComparator(int xk) {
			this.xk = xk;
		}

		@Override
		public int compare(Student a, Student b) {
			return Float.compare(b.scores[xk], a.scores[xk]);
		}
	}
}